package start;


import javax.security.auth.Subject;
import javax.security.auth.login.*;

import netTools.IPManager;
import netTools.PortManager;

import authentication.AuthHandler;
import authentication.SecureServer;
import authorization.FolderMaster;
import authorization.ResourceManager;

import com.sun.xacml.PDP;
import com.sun.xacml.PDPConfig;
import com.sun.xacml.finder.AttributeFinder;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.impl.CurrentEnvModule;
import com.sun.xacml.finder.impl.FilePolicyModule;


import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

/**
 * Master among masters! Master server is the beginning and the end of everything!
 * It performs an initialization phase in which it proves its identity to KDC and obtains valid
 * credentials to do its work. It then initialize the PDP with a valid policy file and Resource Managers
 * as well. After initialization it waits for incoming connections, passing them to Secure Servers 
 * threads which it creates so it can return waiting for new connections.
 * MasterServer also initialize server log so every connection can be tracked.
 * 
 * @author pasquale
 *
 */
public class StrongBoxServer{

	public static final String POLICY_PATH = "policies/";
	public static final String POLICY_FILE = "policy.xml";
	public static final String LOG_PATH = "server_logs/server_log.txt";
	
	//timeouts
	public static final int CONNECTION_TIMEOUT = 120000; //2 minutes of inactivity after connection
	public static final int ACCEPT_TRANSFER_TIMEOUT = 60000; //1 minutes of inactivity for data transfer connection
	public static final int TRANSFER_TIMEOUT = 30000; //30 seconds of inactivity during data trasfer

	public void initServer(String princName,String princPass){
		try {
			lc = new LoginContext("AuthServer",new AuthHandler(princName,princPass));
			lc.login();
		} catch (LoginException e) {
			System.err.println("Service authentication failed!");
			System.exit(-1);
		}

		System.out.println("Service authentication succedeed");

		//policy initialization (PDP)

		FilePolicyModule policyModule = new FilePolicyModule();
		Set<FilePolicyModule> policyModules = new HashSet<FilePolicyModule>();
		PolicyFinder policyFinder = new PolicyFinder();

		policyModule.addPolicy(POLICY_PATH+POLICY_FILE);
		policyModules.add(policyModule);

		policyFinder.setModules(policyModules);

		CurrentEnvModule envMod = new CurrentEnvModule();
		AttributeFinder attrFinder = new AttributeFinder();
		List<CurrentEnvModule> attrModules = new ArrayList<CurrentEnvModule>();
		attrModules.add(envMod);
		attrFinder.setModules(attrModules);
		pdp = new PDP(new PDPConfig(attrFinder,policyFinder,null));

		//managers initialization
		String folders[] = new File(FolderMaster.ROOT_DIR).list();
		managers = new ResourceManager[folders.length];
		for(int i=0;i<managers.length;i++){
			if (!folders[i].startsWith("."))
				managers[i] = new ResourceManager(folders[i],pdp);
		}
		
		System.out.println("PDP and managers initialized");
		
		//logger initialization
		try {
			FileHandler fh = new FileHandler(LOG_PATH,true); //append mode
			fh.setFormatter(new SimpleFormatter());
			logger = Logger.getLogger("service.logger");
			logger.addHandler(fh);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//port manager initialization with default paramethers
		portManager = new PortManager();
		
		//ip manager initialization with default paramethers
		ipManager = new IPManager();
	}

	public void service(int serverPort) throws IOException{
		logger.info("Server started servicing on port "+serverPort);
		
		ServerSocket ss = new ServerSocket(serverPort);
		while(true){
			Socket socket = ss.accept();
			if(!(ipManager.isBanned(socket.getInetAddress()))){
				socket.setSoTimeout(CONNECTION_TIMEOUT);		//2min timeout
				(new Thread(new SecureServer(socket,this))).start();
			}
			else 
				socket.close();
		}	
	}
	
	public PortManager getPortManager(){
		return portManager;
	}
	
	public Subject getSubject(){
		return lc.getSubject();
	}
	
	public ResourceManager[] getResourceManagers(){
		return managers;
	}
	
	public Logger getServerLogger(){
		return logger;
	}
	
	public IPManager getIPManager(){
		return ipManager;
	}
	
	

	public static void main(String[] args) {
		if(args.length != 3){
			System.out.println("Usage: StrongBoxServer <service name> <service password> <port>");
			System.exit(-1);
		}
		String princName = args[0];
		String princPass = args[1];
		int serverPort = Integer.parseInt(args[2]);

		StrongBoxServer server = new StrongBoxServer();
		server.initServer(princName, princPass);

		try {
			server.service(serverPort);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private LoginContext lc;
	private PDP pdp;
	private ResourceManager[] managers;
	private Logger logger;
	private PortManager portManager;
	private IPManager ipManager;

}
